/******************* (C) COPYRIGHT 2022 CHU WEI TENG ***********************************************
**
**------------------ File Info ---------------------------------------------------------------------
** File name:      dwin_fifo.c
** modified Date:  2022-05-10
** Last Version:   V1.0
** Descriptions: 
** 
**------------------ Modfication  log --------------------------------------------------------------
** Date       Who            Rev        Comments
----------------------------------------------------------------------------------------------------
   22/05/10  chuwt@qq.com    V1.00      created

****************************************************************************************************/
#include <string.h>
#include <stdbool.h>
#include "dwin_fifo.h"
#include "dwin_adapt.h"

/* Private define----------------------------------------------------------------------------------*/
#define IS_POWER_OF_2(x)	((x) != 0 && (((x) & ((x) - 1)) == 0))

/* Private typedef---------------------------------------------------------------------------------*/

/* Private variables-------------------------------------------------------------------------------*/

/* public function---------------------------------------------------------------------------------*/

dwin_fifo_t* dwin_fifo_create(uint32_t elem_size, uint32_t capacity)
{
    return_value_if_fail(IS_POWER_OF_2(capacity), NULL);
    
    dwin_fifo_t* fifo = dwin_malloc(sizeof(dwin_fifo_t));

    if (fifo != NULL)
    {
        fifo->elem_ptr = dwin_malloc(elem_size * capacity);
        
        if (fifo->elem_ptr != NULL)
        {
            fifo->in = 0;

            fifo->elem_size = elem_size;
            fifo->capacity = capacity;
            fifo->mask = capacity - 1;
        }
        else
        {
            dwin_free(fifo);
            fifo = NULL;
        }
    }
    return fifo;
}

ret_t dwin_fifo_destory(dwin_fifo_t* fifo)
{
    return_value_if_fail(fifo, RET_BAD_PARAMS);
    return_value_if_fail(fifo->elem_ptr, RET_BAD_PARAMS);
    dwin_free(fifo->elem_ptr);
    dwin_free(fifo);
    return RET_OK;
}

uint32_t dwin_fifo_push(dwin_fifo_t* fifo, void* data)
{
	return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

	if ((fifo->in - fifo->out) < fifo->capacity)
    {
        uint32_t  size = fifo->in & fifo->mask;
		uint8_t * p = (uint8_t *)fifo->elem_ptr + size * fifo->elem_size;
		
		memcpy(p, data, fifo->elem_size);				
		fifo->in++;
		return 1;
    }
	return 0;	
}

uint32_t dwin_fifo_pop(dwin_fifo_t* fifo, void* data)
{
	return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

	if (fifo->in != fifo->out)
    {
        uint32_t  size = fifo->out & fifo->mask;
		uint8_t * p = (uint8_t *)fifo->elem_ptr + size * fifo->elem_size;
		
		memcpy(data, p, fifo->elem_size);				
		fifo->out++;
		return 1;
    }
	return 0;	
}

uint32_t dwin_fifo_peek(dwin_fifo_t* fifo, void* data)
{
	return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

	if (fifo->in != fifo->out)
    {
        uint32_t  size = fifo->out & fifo->mask;
		uint8_t * p = (uint8_t *)fifo->elem_ptr + size * fifo->elem_size;
		
		memcpy(data, p, fifo->elem_size);				
		return 1;
    }
	return 0;	
}

uint32_t dwin_fifo_mutli_push(dwin_fifo_t* fifo, void* data, uint32_t elem_cnt)
{
    return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

    uint32_t count = 0;
    uint8_t* p_data = data;

    while (elem_cnt--)
    {
        if (dwin_fifo_push(fifo, p_data))
        {
            p_data += fifo->elem_size;
            count++;
        }
        else
        {
            break;
        }
    }
    return count;
}

uint32_t dwin_fifo_mutli_pop(dwin_fifo_t* fifo, void* data, uint32_t elem_cnt)
{
    return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

    uint32_t count = 0;
    uint8_t* p_data = data;

    while (elem_cnt--)
    {
        if (dwin_fifo_pop(fifo, p_data))
        {
            p_data += fifo->elem_size;
            count++;
        }
        else
        {
            break;
        }
    }
    return count;
}

uint32_t dwin_fifo_mutli_peek(dwin_fifo_t* fifo, void* data, uint32_t elem_cnt)
{
	return_value_if_fail(fifo, 0);
    return_value_if_fail(fifo->elem_ptr, 0);
    return_value_if_fail(data, 0);

    uint32_t cnt = 0;

	if (fifo->in != fifo->out)
    {
        uint8_t* p_data = data;
        uint32_t out = fifo->out;
        uint32_t min = fifo->in - fifo->out;

        min = DWIN_MIN(elem_cnt, min);

        while (min--)
        {
            uint32_t  size = out & fifo->mask;
            uint8_t * p = (uint8_t *)fifo->elem_ptr + size * fifo->elem_size;
            
            memcpy(p_data, p, fifo->elem_size);	
            p_data += fifo->elem_size;
            out++;
            cnt++;
        }
    }
    return cnt;
}


uint32_t dwin_fifo_get_count(dwin_fifo_t* fifo)
{
    return_value_if_fail(fifo, 0);

    return (fifo->in - fifo->out);
}

ret_t dwin_fifo_clear(dwin_fifo_t* fifo)
{
    return_value_if_fail(fifo, RET_BAD_PARAMS);

    fifo->in = 0;
    fifo->out = 0;
    return RET_OK;
}

uint8_t dwin_fifo_is_empty(dwin_fifo_t* fifo)
{
    return_value_if_fail(fifo, false);

    return (fifo->in == fifo->out);
}

uint8_t dwin_fifo_is_full(dwin_fifo_t* fifo)
{
    return_value_if_fail(fifo, false);
    
    return (((fifo->in + 1) & fifo->mask) == fifo->out);
}
